﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.IO;

namespace ParticleTracker
{
    
    class BubbleForm : Form
    {
        private Button workButton;
        private TextBox xText;
        private TextBox widthText;
        private TrackBar frameSlide;
        private ProgressBar progressBar;
        private ComboBox taskBox;
        private GroupBox groupBox1;
        private TextBox heightText;
        private TextBox yText;
        private TextBox threshText;
        private PictureBox pictureBox1;
        private Label frameNumber;
        private TableLayoutPanel tableLayoutPanel1;
        private TableLayoutPanel tableLayoutPanel2;
        private TextBox r1Text;
        private Label status;
        private Button button1;
        private Label label2;
        private TextBox r2Text;
        private GroupBox groupBox4;
        private GroupBox groupBox3;
        private GroupBox groupBox2;
        private Button play;
        private Timer playTimer;
        private System.ComponentModel.IContainer components;
        private ComboBox options;
        ParticleReel reel;
        private String saveFolderPath = "./_output";
        private String outputFilePrefix = "f";
        double timeElapsed = 0;

        public void LoadFrames()
        {
            string[] filePaths = Directory.GetFiles(@"images","*.bmp");
            outputFilePrefix = filePaths[0].Substring(filePaths[0].IndexOf('\\')+1, 5);
            foreach (string file in filePaths){
                ParticleFrame b = new ParticleFrame(file);
                reel.Frames.Add(b);
            }
            this.frameSlide.Maximum = filePaths.Length - 1;
            this.frameSlide.Minimum = 0;
        }
        public BubbleForm()
        {
            reel = new ParticleReel();
            this.InitializeComponent();
            playTimer.Interval = 100;
            playTimer.Tick += new EventHandler(playTimer_Tick);
            this.LoadFrames();
            this.taskBox.Items.Add("Monochrome");
            this.taskBox.Items.Add("Static");
            this.taskBox.Items.Add("Adaptive");
            this.taskBox.Items.Add("Track-PTV");
            this.taskBox.Items.Add("Track-PIV");
            this.pictureBox1.Image = reel.Frames[frameSlide.Value].OriginalImage;
        }

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.pictureBox1 = new System.Windows.Forms.PictureBox();
            this.workButton = new System.Windows.Forms.Button();
            this.xText = new System.Windows.Forms.TextBox();
            this.widthText = new System.Windows.Forms.TextBox();
            this.frameSlide = new System.Windows.Forms.TrackBar();
            this.progressBar = new System.Windows.Forms.ProgressBar();
            this.taskBox = new System.Windows.Forms.ComboBox();
            this.groupBox1 = new System.Windows.Forms.GroupBox();
            this.options = new System.Windows.Forms.ComboBox();
            this.play = new System.Windows.Forms.Button();
            this.groupBox4 = new System.Windows.Forms.GroupBox();
            this.r1Text = new System.Windows.Forms.TextBox();
            this.r2Text = new System.Windows.Forms.TextBox();
            this.groupBox3 = new System.Windows.Forms.GroupBox();
            this.heightText = new System.Windows.Forms.TextBox();
            this.groupBox2 = new System.Windows.Forms.GroupBox();
            this.yText = new System.Windows.Forms.TextBox();
            this.label2 = new System.Windows.Forms.Label();
            this.button1 = new System.Windows.Forms.Button();
            this.status = new System.Windows.Forms.Label();
            this.threshText = new System.Windows.Forms.TextBox();
            this.frameNumber = new System.Windows.Forms.Label();
            this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
            this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
            this.playTimer = new System.Windows.Forms.Timer(this.components);
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.frameSlide)).BeginInit();
            this.groupBox1.SuspendLayout();
            this.groupBox4.SuspendLayout();
            this.groupBox3.SuspendLayout();
            this.groupBox2.SuspendLayout();
            this.tableLayoutPanel1.SuspendLayout();
            this.tableLayoutPanel2.SuspendLayout();
            this.SuspendLayout();
            // 
            // pictureBox1
            // 
            this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.pictureBox1.Location = new System.Drawing.Point(3, 3);
            this.pictureBox1.Name = "pictureBox1";
            this.pictureBox1.Size = new System.Drawing.Size(375, 271);
            this.pictureBox1.TabIndex = 0;
            this.pictureBox1.TabStop = false;
            // 
            // workButton
            // 
            this.workButton.Location = new System.Drawing.Point(242, 73);
            this.workButton.Name = "workButton";
            this.workButton.Size = new System.Drawing.Size(93, 189);
            this.workButton.TabIndex = 2;
            this.workButton.Text = "Work";
            this.workButton.UseVisualStyleBackColor = true;
            this.workButton.Click += new System.EventHandler(this.workButton_Click);
            // 
            // xText
            // 
            this.xText.Location = new System.Drawing.Point(6, 19);
            this.xText.Name = "xText";
            this.xText.Size = new System.Drawing.Size(100, 20);
            this.xText.TabIndex = 3;
            this.xText.Text = "0";
            // 
            // widthText
            // 
            this.widthText.Location = new System.Drawing.Point(6, 18);
            this.widthText.Name = "widthText";
            this.widthText.Size = new System.Drawing.Size(100, 20);
            this.widthText.TabIndex = 4;
            // 
            // frameSlide
            // 
            this.frameSlide.Dock = System.Windows.Forms.DockStyle.Fill;
            this.frameSlide.Location = new System.Drawing.Point(3, 280);
            this.frameSlide.Name = "frameSlide";
            this.frameSlide.Size = new System.Drawing.Size(375, 60);
            this.frameSlide.TabIndex = 5;
            this.frameSlide.ValueChanged += new System.EventHandler(this.frameSlide_ValueChanged);
            // 
            // progressBar
            // 
            this.progressBar.Dock = System.Windows.Forms.DockStyle.Fill;
            this.progressBar.Location = new System.Drawing.Point(3, 33);
            this.progressBar.Name = "progressBar";
            this.progressBar.Size = new System.Drawing.Size(338, 24);
            this.progressBar.TabIndex = 6;
            // 
            // taskBox
            // 
            this.taskBox.FormattingEnabled = true;
            this.taskBox.Location = new System.Drawing.Point(10, 212);
            this.taskBox.Name = "taskBox";
            this.taskBox.Size = new System.Drawing.Size(107, 21);
            this.taskBox.TabIndex = 7;
            // 
            // groupBox1
            // 
            this.groupBox1.Controls.Add(this.options);
            this.groupBox1.Controls.Add(this.play);
            this.groupBox1.Controls.Add(this.groupBox4);
            this.groupBox1.Controls.Add(this.groupBox3);
            this.groupBox1.Controls.Add(this.groupBox2);
            this.groupBox1.Controls.Add(this.label2);
            this.groupBox1.Controls.Add(this.button1);
            this.groupBox1.Controls.Add(this.status);
            this.groupBox1.Controls.Add(this.threshText);
            this.groupBox1.Controls.Add(this.taskBox);
            this.groupBox1.Controls.Add(this.workButton);
            this.groupBox1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.groupBox1.Location = new System.Drawing.Point(384, 3);
            this.groupBox1.Name = "groupBox1";
            this.groupBox1.Size = new System.Drawing.Size(344, 271);
            this.groupBox1.TabIndex = 8;
            this.groupBox1.TabStop = false;
            this.groupBox1.Text = "Options";
            // 
            // options
            // 
            this.options.FormattingEnabled = true;
            this.options.Items.AddRange(new object[] {
            "sdOverlap - eOverlap",
            "sdOverlap - eMiniature",
            "sdNoOverlap - eOverlap",
            "sdNoOverlap - eMiniature"});
            this.options.Location = new System.Drawing.Point(123, 213);
            this.options.Name = "options";
            this.options.Size = new System.Drawing.Size(109, 21);
            this.options.TabIndex = 21;
            // 
            // play
            // 
            this.play.Location = new System.Drawing.Point(242, 19);
            this.play.Name = "play";
            this.play.Size = new System.Drawing.Size(93, 48);
            this.play.TabIndex = 20;
            this.play.Text = "Play";
            this.play.UseVisualStyleBackColor = true;
            this.play.Click += new System.EventHandler(this.play_Click);
            // 
            // groupBox4
            // 
            this.groupBox4.Controls.Add(this.r1Text);
            this.groupBox4.Controls.Add(this.r2Text);
            this.groupBox4.Location = new System.Drawing.Point(10, 125);
            this.groupBox4.Name = "groupBox4";
            this.groupBox4.Size = new System.Drawing.Size(226, 56);
            this.groupBox4.TabIndex = 19;
            this.groupBox4.TabStop = false;
            this.groupBox4.Text = "R Values";
            // 
            // r1Text
            // 
            this.r1Text.Location = new System.Drawing.Point(6, 19);
            this.r1Text.Name = "r1Text";
            this.r1Text.Size = new System.Drawing.Size(100, 20);
            this.r1Text.TabIndex = 11;
            // 
            // r2Text
            // 
            this.r2Text.Location = new System.Drawing.Point(112, 18);
            this.r2Text.Name = "r2Text";
            this.r2Text.Size = new System.Drawing.Size(100, 20);
            this.r2Text.TabIndex = 15;
            // 
            // groupBox3
            // 
            this.groupBox3.Controls.Add(this.widthText);
            this.groupBox3.Controls.Add(this.heightText);
            this.groupBox3.Location = new System.Drawing.Point(11, 73);
            this.groupBox3.Name = "groupBox3";
            this.groupBox3.Size = new System.Drawing.Size(225, 50);
            this.groupBox3.TabIndex = 18;
            this.groupBox3.TabStop = false;
            this.groupBox3.Text = "W/H";
            // 
            // heightText
            // 
            this.heightText.Location = new System.Drawing.Point(112, 18);
            this.heightText.Name = "heightText";
            this.heightText.Size = new System.Drawing.Size(100, 20);
            this.heightText.TabIndex = 9;
            // 
            // groupBox2
            // 
            this.groupBox2.Controls.Add(this.xText);
            this.groupBox2.Controls.Add(this.yText);
            this.groupBox2.Location = new System.Drawing.Point(10, 19);
            this.groupBox2.Name = "groupBox2";
            this.groupBox2.Size = new System.Drawing.Size(222, 48);
            this.groupBox2.TabIndex = 17;
            this.groupBox2.TabStop = false;
            this.groupBox2.Text = "X/Y";
            // 
            // yText
            // 
            this.yText.Location = new System.Drawing.Point(112, 19);
            this.yText.Name = "yText";
            this.yText.Size = new System.Drawing.Size(100, 20);
            this.yText.TabIndex = 8;
            this.yText.Text = "0";
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(7, 190);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(40, 13);
            this.label2.TabIndex = 16;
            this.label2.Text = "Thresh";
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(10, 239);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(104, 23);
            this.button1.TabIndex = 13;
            this.button1.Text = "Show Velocities";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // status
            // 
            this.status.AutoSize = true;
            this.status.Location = new System.Drawing.Point(120, 244);
            this.status.Name = "status";
            this.status.Size = new System.Drawing.Size(37, 13);
            this.status.TabIndex = 12;
            this.status.Text = "Status";
            // 
            // threshText
            // 
            this.threshText.Location = new System.Drawing.Point(58, 187);
            this.threshText.Name = "threshText";
            this.threshText.Size = new System.Drawing.Size(174, 20);
            this.threshText.TabIndex = 10;
            // 
            // frameNumber
            // 
            this.frameNumber.Anchor = System.Windows.Forms.AnchorStyles.Left;
            this.frameNumber.AutoSize = true;
            this.frameNumber.Location = new System.Drawing.Point(3, 8);
            this.frameNumber.Name = "frameNumber";
            this.frameNumber.Size = new System.Drawing.Size(65, 13);
            this.frameNumber.TabIndex = 9;
            this.frameNumber.Text = "Frame Index";
            // 
            // tableLayoutPanel1
            // 
            this.tableLayoutPanel1.ColumnCount = 2;
            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 350F));
            this.tableLayoutPanel1.Controls.Add(this.pictureBox1, 0, 0);
            this.tableLayoutPanel1.Controls.Add(this.groupBox1, 1, 0);
            this.tableLayoutPanel1.Controls.Add(this.frameSlide, 0, 1);
            this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel2, 1, 1);
            this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
            this.tableLayoutPanel1.Name = "tableLayoutPanel1";
            this.tableLayoutPanel1.RowCount = 2;
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 81.04956F));
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 18.95044F));
            this.tableLayoutPanel1.Size = new System.Drawing.Size(731, 343);
            this.tableLayoutPanel1.TabIndex = 10;
            // 
            // tableLayoutPanel2
            // 
            this.tableLayoutPanel2.ColumnCount = 1;
            this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
            this.tableLayoutPanel2.Controls.Add(this.frameNumber, 0, 0);
            this.tableLayoutPanel2.Controls.Add(this.progressBar, 0, 1);
            this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tableLayoutPanel2.Location = new System.Drawing.Point(384, 280);
            this.tableLayoutPanel2.Name = "tableLayoutPanel2";
            this.tableLayoutPanel2.RowCount = 2;
            this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
            this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
            this.tableLayoutPanel2.Size = new System.Drawing.Size(344, 60);
            this.tableLayoutPanel2.TabIndex = 9;
            // 
            // BubbleForm
            // 
            this.ClientSize = new System.Drawing.Size(731, 343);
            this.Controls.Add(this.tableLayoutPanel1);
            this.Name = "BubbleForm";
            this.Text = "Particle Tracker - m475";
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.frameSlide)).EndInit();
            this.groupBox1.ResumeLayout(false);
            this.groupBox1.PerformLayout();
            this.groupBox4.ResumeLayout(false);
            this.groupBox4.PerformLayout();
            this.groupBox3.ResumeLayout(false);
            this.groupBox3.PerformLayout();
            this.groupBox2.ResumeLayout(false);
            this.groupBox2.PerformLayout();
            this.tableLayoutPanel1.ResumeLayout(false);
            this.tableLayoutPanel1.PerformLayout();
            this.tableLayoutPanel2.ResumeLayout(false);
            this.tableLayoutPanel2.PerformLayout();
            this.ResumeLayout(false);

        }

        private void workButton_Click(object sender, EventArgs e)
        {
            string task = taskBox.Text;
            Rectangle r;
            try
            {
                r = new Rectangle(
                    Int32.Parse(xText.Text),
                    Int32.Parse(yText.Text),
                    Int32.Parse(widthText.Text),
                    Int32.Parse(heightText.Text)
                        );
            }
            catch (Exception)
            {
                r = new Rectangle(0, 0, this.pictureBox1.Image.Width, this.pictureBox1.Image.Height);
            }
            this.xText.Text = r.X.ToString();
            this.yText.Text = r.Y.ToString();
            this.widthText.Text = r.Width.ToString();
            this.heightText.Text = r.Height.ToString();
            ParticleFrame frame = reel.Frames[frameSlide.Value];
            frame.Area = r;
            if (task == "Monochrome")
            {
                frame.Monochrome();
            }
            if (task == "Adaptive")
            {
                frame.AdaptiveThreshold();
            }
            if (task == "Static")
            {
                frame.StaticThreshold();
            }
            if (task == "Track-PTV")
            {
                this.ReadyFrames();
                try
                {
                    DateTime start = DateTime.Now; 
                    new ParticleTrackingVelocimetry(reel, Int32.Parse(r1Text.Text), Int32.Parse(r2Text.Text)).Run();
                    timeElapsed = start.Subtract(DateTime.Now).TotalMilliseconds;
                }
                catch (Exception)
                {
                    DateTime start = DateTime.Now; 
                    new ParticleTrackingVelocimetry(reel).Run();
                    timeElapsed = DateTime.Now.Subtract(start).TotalMilliseconds;
                }
                saveOutput();
                progressBar.Value++;
                AddVelocities();
                progressBar.Value++;
                status.Text = "Done Tracking.";
            }
            if (task == "Track-PIV")
            {
                this.ReadyFrames();
                ParticleImageVelocimetry piv;
                try
                {
                    piv = new ParticleImageVelocimetry(reel, Int32.Parse(r1Text.Text), new Size(Int32.Parse(widthText.Text), Int32.Parse(heightText.Text)));
                }
                catch
                {
                    piv = new ParticleImageVelocimetry(reel);
                }
                if (options.Text == "sdNoOverlap - eMiniature")
                {
                    piv.divStyle = SubDivisionStyle.NONOVERLAP;
                    piv.edgeStyle = EdgeHandling.MINIATURE;
                }
                else if (options.Text == "sdNoOverlap - eOverlap")
                {
                    piv.divStyle = SubDivisionStyle.NONOVERLAP;
                    piv.edgeStyle = EdgeHandling.OVERLAP;
                }
                else if (options.Text == "sdOverlap - eMiniature")
                {
                    piv.divStyle = SubDivisionStyle.OVERLAP;
                    piv.edgeStyle = EdgeHandling.MINIATURE;
                }
                else if (options.Text == "sdOverlap - eOverlap")
                {
                    piv.divStyle = SubDivisionStyle.OVERLAP;
                    piv.edgeStyle = EdgeHandling.OVERLAP;
                }
                else
                {
                    piv.divStyle = SubDivisionStyle.NONOVERLAP;
                    piv.edgeStyle = EdgeHandling.MINIATURE;
                }
                piv.Overlap = Double.Parse(r2Text.Text);
                DateTime start = DateTime.Now; 
                piv.Run();
                timeElapsed = DateTime.Now.Subtract(start).TotalMilliseconds;
                saveOutput();
                progressBar.Value++;
                AddVelocities();
                progressBar.Value++;
                status.Text = "Done Tracking.";
            }
            this.pictureBox1.Refresh();
        }

        private void saveOutput()
        {
            velocitiesToTxt();
            saveImages();
        }
        private void velocitiesToTxt()
        {
            
            TextWriter tw = new StreamWriter(saveFolderPath + "/"+this.FilenamePrefix+"velocities.txt", false);
            tw.WriteLine("Task = " + taskBox.Text + tw.NewLine +
                "r1 = " + r1Text.Text + tw.NewLine +
                "r2 = " + r2Text.Text + tw.NewLine +
                "width = " + widthText.Text + tw.NewLine +
                "height = " + heightText.Text + tw.NewLine +
                "options = " + options.Text + tw.NewLine + 
                "elapsed = " + String.Format("{0:0,0.########}",(this.timeElapsed/1000.0)));
                
            for (int i = 0; i < reel.Frames.Count; i++)
            {
                //tw.WriteLine(tw.NewLine + "Frame " + i);
                //tw.WriteLine("-----------------");
                List<Piece> ps = reel.Frames[i].Pieces;
                foreach (Piece p in ps)
                {
                    tw.WriteLine(String.Format("{0},{1},{2},{3},{4},{5},{6}",i,p.Center.X, p.Center.Y, p.velocity.v.X, p.velocity.v.Y, p.velocity.isSuspect, p.velocity.confidence));
                }
                //    tw.WriteLine(p.Center + "\t" + 
                //        p.velocity.v + "\t" + 
                //        p.velocity.isSuspect + "\t" + 
                //        p.velocity.confidence);
                //}
                
            }
            tw.Close();
        }
        private String FilenamePrefix
        {
            get
            {
                return String.Format("{0}-{1}x{2}-{3}-{4}-{5}-{6}", 
                    this.outputFilePrefix, 
                    this.widthText.Text, 
                    this.heightText.Text, 
                    this.r1Text.Text, 
                    this.r2Text.Text, 
                    options.Text, 
                    taskBox.Text);
            }
        }
        private void saveImages()
        {
            for (int i = 0; i < reel.Frames.Count; i++)
            {
                frameSlide.Value = i;
                AddVelocities();
            }
        }

        private void ReadyFrames()
        {
            progressBar.Maximum = reel.Frames.Count * 2 + 2;
            progressBar.Value = 0;
            //reel.Track();
            foreach (ParticleFrame f in reel.Frames)
            {
                f.Pieces = new List<Piece>();
                f.Area = f.RectangleFull;
                f.Monochrome();
                progressBar.Value++;
                f.AdaptiveThreshold();
                progressBar.Value++;
            }
        }
        private void AddVelocities()
        {
            int i = frameSlide.Value;
            List<Piece> pieces = reel.Frames[i].Pieces;
            using (Graphics g = Graphics.FromImage(pictureBox1.Image))
            {
                foreach (Piece p in pieces)
                {
                    if (p.velocity.confidence < .5) continue;
                    g.DrawLine(
                        p.velocity.isSuspect?new Pen(Color.Red,2):new Pen(Color.Green,2), 
                        new Point(p.Center.X, p.Center.Y),
                        new Point(
                            (int)(p.Center.X + 2*p.velocity.v.X),
                            (int)(p.Center.Y + 2*p.velocity.v.Y)
                        )
                    );
                    
                }
            }
            status.Text = "Done";
            this.pictureBox1.Refresh();
            (new Bitmap(pictureBox1.Image)).Save(saveFolderPath + "/" + FilenamePrefix + "Frame_" + i + ".bmp"); ;
        }

        private void frameSlide_ValueChanged(object sender, EventArgs e)
        {
            this.pictureBox1.Image = reel.Frames[frameSlide.Value].OriginalImage;
            this.frameNumber.Text = frameSlide.Value.ToString();
            this.widthText.Text = this.pictureBox1.Image.Width.ToString();
            this.heightText.Text = this.pictureBox1.Image.Height.ToString();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            AddVelocities();
        }

        private void play_Click(object sender, EventArgs e)
        {
            if (playTimer.Enabled)
            {
                playTimer.Stop();
                return;
            }
            playTimer.Start();
        }

        void playTimer_Tick(object sender, EventArgs e)
        {
            if (this.frameSlide.Value == this.frameSlide.Maximum)
                this.frameSlide.Value = 0;
            else this.frameSlide.Value = this.frameSlide.Value + 1;
            this.AddVelocities();
        }

    }
    class Program
    {
        static void Main(string[] args)
        {
            BubbleForm f = new BubbleForm();
            f.ShowDialog();
        }
    }
}
